package com.youhome.api.config;


import com.youhome.common.response.Result;
import com.youhome.common.response.ResultCode;
import com.youhome.service.model.Company;
import com.youhome.service.service.AuthenticationCompanyTokenService;
import com.youhome.service.service.CompanyService;
import com.youhome.service.service.RedisService;
import com.youhome.utils.IPUtils;
import com.youhome.utils.JsonUtils;
import com.youhome.utils.TokenUtils;
import com.youhome.utils.ValidationUtils;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
 * @Auther: Tangzhiqiang
 * @Date: 2019/1/13 21:18
 * @Description: OncePerRequestFilter 确保在一次请求只通过一次filter，而不需要重复执行。
 */
// TODO 还要实现 token 缓存
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    protected HttpServletRequest request;
    @Value("${app.token}")
    private String app_token;
    @Autowired
    private AuthenticationCompanyTokenService authenticationCompanyTokenService;
    @Autowired
    private RequestLimit requestLimit;
    @Autowired
    private RedisService redisService;
    @Autowired
    private CompanyService companyService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        Boolean isFilter = true;
        if (!StringUtils.isEmpty(token)) {
            isFilter= checkToken(token,request,response,isFilter);
//            System.err.println("token:" + token);
//            Long tokenId = null;
//            Result result = null;
//            try {
//                Map<String, Object> claims = TokenUtils.parserToken(token);
//                String object = claims.get("TokenId").toString();
//                tokenId = Long.valueOf(object);
//            } catch (NullPointerException e) {
//                log.error("NullPointerException", e);
//                result = new Result(ResultCode.INVALID_TOKEN_FORMAT);
//            } catch (MalformedJwtException e) {
//                log.error("an error occured during getting username from token", e);
//                result = new Result(ResultCode.INVALID_TOKEN_FORMAT);
//            } catch (ExpiredJwtException e) {
//                log.warn("the token is expired and not valid anymore", e);
//                result = new Result(ResultCode.INVALID_TOKEN_EXPIRED);
//            } catch (SignatureException e) {
//                log.error("Authentication Failed. Username or Password not valid.");
//                result = new Result(ResultCode.INVALID_TOKEN_SIGNATURE);
//            }
//
//            if (tokenId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
//                UserDetails userDetails = authenticationCompanyTokenService.loadUserByUsername(tokenId);
//                if (!userDetails.isEnabled()) {
//                    isFilter = false;
//                    result = new Result(ResultCode.INVALID_TOKEN_FORBIDDEN);
//                }
//                if (TokenUtils.isExpired(token)) {
//                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
//                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
//                    SecurityContextHolder.getContext().setAuthentication(authentication);
//                } else {
//                    isFilter = false;
//                    result = new Result(ResultCode.INVALID_TOKEN);
//                }
//            } else {
//                isFilter = false;
//                result = new Result(ResultCode.INVALID_TOKEN_EXPIRED);
//            }
//
//            if (result != null && result.getStatus() > 0) {// 验证失败
//                try {
//                    response.setCharacterEncoding("utf-8");
//                    response.setContentType("application/json; charset=utf-8");
//                    PrintWriter out = response.getWriter();
//                    out.print(JsonUtils.bean2Json(result));
//                    out.flush();
//                    out.close();
//                } catch (UnsupportedEncodingException e) {
//                    logger.error("过滤器返回信息失败:" + e.getMessage(), e);
//                } catch (IOException e) {
//                    logger.error("过滤器返回信息失败:" + e.getMessage(), e);
//                }
//                return;
//            }
        } else {
            System.err.println("sign");
            if (ValidationUtils.validateSign(request)) {
                //通过授权
                //isFilter = true;
                token = app_token;
                isFilter= checkToken(token,request,response,isFilter);
            }
        }
        if (isFilter && !StringUtils.isEmpty(token)) {
            speedLimit(token);
        }
        if (isFilter) {
            chain.doFilter(request, response);
        }
    }

    public Boolean checkToken(String token,HttpServletRequest request,HttpServletResponse response,Boolean isFilter){

        System.err.println("token:" + token);
        Long tokenId = null;
        Result result = null;
        try {
            Map<String, Object> claims = TokenUtils.parserToken(token);
            String object = claims.get("TokenId").toString();
            tokenId = Long.valueOf(object);
        } catch (NullPointerException e) {
            log.error("NullPointerException", e);
            result = new Result(ResultCode.INVALID_TOKEN_FORMAT);
        } catch (MalformedJwtException e) {
            log.error("an error occured during getting username from token", e);
            result = new Result(ResultCode.INVALID_TOKEN_FORMAT);
        } catch (ExpiredJwtException e) {
            log.warn("the token is expired and not valid anymore", e);
            result = new Result(ResultCode.INVALID_TOKEN_EXPIRED);
        } catch (SignatureException e) {
            log.error("Authentication Failed. Username or Password not valid.");
            result = new Result(ResultCode.INVALID_TOKEN_SIGNATURE);
        }

        if (tokenId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = authenticationCompanyTokenService.loadUserByUsername(tokenId);
            System.err.println("userDetails===>>"+userDetails.toString());
            if (!userDetails.isEnabled()) {
                isFilter = false;
                result = new Result(ResultCode.INVALID_TOKEN_FORBIDDEN);
            }
            if (TokenUtils.isExpired(token)) {
                System.err.println("isExpired==");
                System.err.println("token=="+token);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            } else {
                isFilter = false;
                result = new Result(ResultCode.INVALID_TOKEN);
            }
        } else {
            isFilter = false;
            result = new Result(ResultCode.INVALID_TOKEN_EXPIRED);
        }

        if (result != null && result.getStatus() > 0) {// 验证失败
            try {
                response.setCharacterEncoding("utf-8");
                response.setContentType("application/json; charset=utf-8");
                PrintWriter out = response.getWriter();
                out.print(JsonUtils.bean2Json(result));
                out.flush();
                out.close();
            } catch (UnsupportedEncodingException e) {
                logger.error("过滤器返回信息失败:" + e.getMessage(), e);
            } catch (IOException e) {
                logger.error("过滤器返回信息失败:" + e.getMessage(), e);
            }
        }

        return isFilter;
    }

    /**
     * 限速访问
     *
     * @param token
     */
    public void speedLimit(String token) {
        System.err.println("speedLimit==token===>>" + token);
        Map<String, Object> map = TokenUtils.parserToken(token);
        Integer tokenId = (Integer) map.get("TokenId");
        Integer companyId = (Integer) map.get("CompanyId");

        System.err.println("TokenId:" + tokenId);

        // ====================并发控制 begin====================

        String ip = new IPUtils().getRemoteAddrIp(request);
        String url = request.getRequestURL().toString();
        String token_key = "request_limit_" + tokenId;
        String company_key = "company_" + companyId;

        if (redisService.get(token_key) == null || ((int) redisService.get(token_key) == 0)) {
            redisService.set(token_key, 1, requestLimit.getLimitTime());
        } else {
            long expireTime = redisService.getExpireTime(token_key);
            redisService.set(token_key, (int) redisService.get(token_key) + 1, expireTime);
        }


        int count = (int) redisService.get(token_key);
        if (count > requestLimit.getLimitCount()) {
            log.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + requestLimit.getLimitCount() + "]");
            throw new BadCredentialsException("HTTP请求超出设定的限制");
        }

        // ====================并发控制 end====================
        //获取 redis 中 company 信息
        if (redisService.get(company_key) == null) {
            redisService.set(company_key, companyService.selectByPrimaryKey(Long.valueOf(companyId)));
        }
        Company company = (Company) redisService.get(company_key);

        //System.err.println("1111111111=========>>:" + company.toString());
        int limit = company.getLimit();
        if (limit <= 0) {
            throw new CredentialsExpiredException("接口访问量已用完");
        }
        company.setLimit(limit - 1); //访问量减1
        redisService.set(company_key, company); //修改company  信息
        //System.err.println("2222222222=========>>:" + company.toString());
        //System.err.println("app_key:" + app_key);

        companyService.updateMinusLimitByAppKey(company.getAppKey()); //更改数据库中 访问量
    }
}
